// Copyright Gary Evans 2006-2007.

using System;
using System.Collections.Generic;
using System.Text;

namespace Taumuon.Jabuka.Physics
{
    /// <summary>
    /// Represents a Vector in 3 dimensions.
    /// Can represent a point, or a vector from the origin.
    /// </summary>
    public class Vector : ICloneable
    {
        #region Private Member Variables

        private double x = 0.0;
        private double y = 0.0;
        private double z = 0.0;

        private const double Tolerance = 1e-8;

        #endregion Private Member Variables

        #region Object Lifetime

		/// <summary>
		/// Default constructor, initialises all values to zero.
		/// </summary>
		public Vector()
		{
		}

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="x">Initial x location</param>
        /// <param name="y">Initial y location</param>
        /// <param name="z">Initial z location</param>
        public Vector(double x, double y, double z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        #endregion Object Lifetime

        #region Accessors

        /// <summary>
        /// Returns the vector's X dimension value.
        /// </summary>
        public double X
        {
            get { return x; }
            set { x = value; }
        }

        /// <summary>
        /// Returns the vector's X dimension value.
        /// </summary>
        public double Y
        {
            get { return y; }
            set { y = value; }
        }

        /// <summary>
        /// Returns the vector's Z dimension value.
        /// </summary>
        public double Z
        {
            get { return z; }
            set { z = value; }
        }

        /// <summary>
        /// Returns the vector's magnitude.
        /// </summary>
        public double Magnitude
        {
            get
            {
                return Math.Sqrt((x * x) + (y * y) + (z * z));
            }
        }

        #endregion Accessors

        #region Public Methods

		/// <summary>
		/// Sets the values of the vector components.
		/// </summary>
		public void SetValues(double x, double y, double z)
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}

        /// <summary>
        /// Returns the dot product of this vector against the right hand side operand.
        /// </summary>
        /// <param name="rhs">The vector to perform the dot product with.</param>
        /// <returns>The result of the operation</returns>
        public double DotProduct(Vector rhs)
        {
            return (x * rhs.x) + (y * rhs.y) + (z * rhs.z);
        }

		/// <summary>
		/// Returns the cross product of this vector against the right hand side operand.
		/// </summary>
		/// <param name="rhs">The vector to perform the cross product with.</param>
		/// <returns>The result of the operation, leaving the operands unchanged.</returns>
		public Vector CrossProduct(Vector rhs)
		{
			return new Vector(this.y * rhs.z - this.z * rhs.y,
					-this.x * rhs.z + this.z * rhs.x,
					this.x * rhs.y - this.y * rhs.x);
		}

		/// <summary>
		/// Returns a normalised vector in the direction of this vector.
		/// </summary>
        /// <returns>The result of the operation, leaving the operands unchanged.</returns>
        /// <remarks>If this vector has zero magnitude, then a zero vector is returned.</remarks>
		public Vector Normalise()
		{
			double magnitude = Math.Sqrt((x * x) + (y * y) + (z * z));

            // Prevent divide by zero.
            if (magnitude < Tolerance)
			{
                return new Vector();
			}

            return new Vector(x / magnitude, y / magnitude, z / magnitude);
		}

        #endregion Public Methods

        #region ICloneable Members

        /// <summary>
        /// Performs a deep copy.
        /// </summary>
        /// <returns>A copy of the object.</returns>
        public object Clone()
        {
            return this.MemberwiseClone() as Vector;
        }

        #endregion ICloneable Members

        #region Operators

        /// <summary>
        /// Overloaded addition operator. Adds the right hand
        ///  operand to the left hand operand.
        /// </summary>
        /// <param name="lhs">The left-hand operand</param>
        /// <param name="rhs">The right-hand operand</param>
        /// <returns>
        /// Result of addition, leaving the operands unchanged.
        /// </returns>
        public static Vector operator +(Vector lhs, Vector rhs)
        {
            return new Vector(lhs.X + rhs.X, lhs.Y + rhs.Y,
                                                lhs.Z + rhs.Z);
        }

        /// <summary>
        /// Overloaded subtraction operator. Subtracts the right
        ///  hand operand from the left hand operand.
        /// </summary>
        /// <param name="lhs">The left-hand operand</param>
        /// <param name="rhs">The right-hand operand</param>
        /// <returns>
        /// Result of subtraction, leaving the operands unchanged.
        /// </returns>
        // TODO: if implement unary - operator, then maybe make use
        //  of this (and similar for the unary operator +)
        public static Vector operator -(Vector lhs, Vector rhs)
        {
            return new Vector(lhs.X - rhs.X, lhs.Y - rhs.Y,
                                                lhs.Z - rhs.Z);
        }

        /// <summary>
        /// Overloaded multiplication operator, for a vector and
        /// a scalar on the right hand side of the operation.
        /// </summary>
        /// <param name="lhs">The left-hand operand</param>
        /// <param name="rhs">The right-hand operand</param>
        /// <returns>
        /// Result of multiplication, leaving operands unchanged.
        /// </returns>
        public static Vector operator *(Vector lhs, double rhs)
        {
            return new Vector(lhs.X * rhs, lhs.Y * rhs, lhs.Z * rhs);
        }

        /// <summary>
        /// Overloaded multiplication operator, for a vector and
        /// a scalar on the left hand side of the operation.
        /// </summary>
        /// <param name="lhs">The left-hand operand</param>
        /// <param name="rhs">The right-hand operand</param>
        /// <returns>
        /// Result of multiplication, leaving operands unchanged.
        /// </returns>
        public static Vector operator *(double lhs, Vector rhs)
        {
            return new Vector(rhs.X * lhs, rhs.Y * lhs, rhs.Z * lhs);
        }

        #endregion Operators
    }
}
